import crypto from 'node:crypto'
import http from 'node:http'
import https from 'node:https'
import { URL } from 'node:url'
import WebSocket from 'ws'
import { createLogger, type ScopedLogger } from '#/logger'

interface TTSRequestParams {
  text: string
  voiceConfig: AIAssistConfig
}

interface AliyunTTSResponse {
  code?: string
  message?: string
}

/**
 * 阿里云 CosyVoice TTS 服务
 */
export class TTSService {
  private audioContext: AudioContext | null = null
  private currentAudio: AudioBufferSourceNode | null = null
  private logger: ScopedLogger

  constructor() {
    // 在浏览器环境下初始化 AudioContext
    if (typeof window !== 'undefined' && 'AudioContext' in window) {
      this.audioContext = new AudioContext()
    }
    this.logger = createLogger('AI助播').scope('TTS')
  }

  /**
   * 合成语音并播放
   */
  async synthesizeAndPlay(params: TTSRequestParams): Promise<{
    success: boolean
    error?: string
  }> {
    try {
      this.logger.info(`开始语音合成，文本: ${params.text}`)
      const { text, voiceConfig } = params

      // 根据厂商验证配置
      if (!voiceConfig.provider || voiceConfig.provider === 'aliyun') {
        if (!voiceConfig.aliyun?.accessKeyId || !voiceConfig.aliyun?.accessKeySecret || !voiceConfig.aliyun?.appKey) {
          this.logger.error('配置验证失败：未配置阿里云密钥或AppKey')
          return { success: false, error: 'AccessKey ID、AccessKey Secret 或 Appkey 未配置' }
        }
        this.logger.info('配置验证通过，开始调用阿里云 TTS API')
      } else if (voiceConfig.provider === 'tencent') {
        if (!voiceConfig.tencent?.secretId || !voiceConfig.tencent?.secretKey) {
          this.logger.error('配置验证失败：未配置腾讯云密钥')
          return { success: false, error: 'SecretId 或 SecretKey 未配置' }
        }
        this.logger.info('配置验证通过，开始调用腾讯云 TTS API')
      } else if (voiceConfig.provider === 'edge') {
        if (!voiceConfig.edge?.voice) {
          this.logger.error('配置验证失败：未配置 Edge-TTS 语音')
          return { success: false, error: 'Edge-TTS 语音未配置' }
        }
        this.logger.info('配置验证通过，开始调用 Edge-TTS API')
      }

      // 根据厂商调用对应的 TTS API
      let audioData: Buffer
      if (!voiceConfig.provider || voiceConfig.provider === 'aliyun') {
        audioData = await this.callAliyunTTS(text, voiceConfig)
      } else if (voiceConfig.provider === 'tencent') {
        audioData = await this.callTencentTTS(text, voiceConfig)
      } else if (voiceConfig.provider === 'edge') {
        audioData = await this.callEdgeTTS(text, voiceConfig)
      } else {
        throw new Error(`不支持的语音服务厂商: ${voiceConfig.provider}`)
      }
      
      this.logger.info(`TTS API 调用成功，音频数据大小: ${audioData.length} bytes`)

      // 播放音频
      this.logger.info('开始播放音频')
      await this.playAudio(audioData)
      
      this.logger.success('语音播放完成')

      return { success: true }
    } catch (error) {
      this.logger.error(`TTS 合成失败: ${error instanceof Error ? error.message : String(error)}`)
      return {
        success: false,
        error: error instanceof Error ? error.message : String(error),
      }
    }
  }

  /**
   * 测试配置是否有效
   */
  async testConfig(config: AIAssistConfig): Promise<{
    success: boolean
    error?: string
  }> {
    try {
      const provider = config.provider || 'aliyun'
      const providerName = 
        provider === 'aliyun' ? '阿里云 CosyVoice' : 
        provider === 'tencent' ? '腾讯云 TTS' : 
        'Edge-TTS'
      
      this.logger.info('========== TTS 配置测试 ==========')
      this.logger.info(`语音服务厂商: ${providerName}`)
      
      if (provider === 'aliyun') {
        this.logger.info('阿里云配置检查:')
        this.logger.info(`- AccessKey ID: ${config.aliyun.accessKeyId?.substring(0, 10)}...`)
        this.logger.info(`- AccessKey Secret: ${config.aliyun.accessKeySecret?.substring(0, 10)}...`)
        this.logger.info(`- AppKey: ${config.aliyun.appKey?.substring(0, 10)}...`)
        this.logger.info(`- Endpoint: ${config.aliyun.endpoint}`)
      } else if (provider === 'tencent') {
        this.logger.info('腾讯云配置检查:')
        this.logger.info(`- SecretId: ${config.tencent?.secretId?.substring(0, 10)}...`)
        this.logger.info(`- SecretKey: ${config.tencent?.secretKey?.substring(0, 10)}...`)
        this.logger.info(`- Region: ${config.tencent?.region || 'ap-guangzhou（默认）'}`)
      } else if (provider === 'edge') {
        this.logger.info('Edge-TTS 配置检查:')
        this.logger.info(`- Voice: ${config.edge?.voice || 'zh-CN-XiaoxiaoNeural（默认）'}`)
        this.logger.info(`- API地址: ${config.edge?.apiUrl || 'http://localhost:5000（默认）'}`)
      }
      
      const testText = '这是一个测试'
      
      // 根据厂商选择调用对应的 API
      if (provider === 'aliyun') {
        this.logger.info('正在调用阿里云 TTS API...')
        await this.callAliyunTTS(testText, config)
      } else if (provider === 'tencent') {
        this.logger.info('正在调用腾讯云 TTS API...')
        await this.callTencentTTS(testText, config)
      } else if (provider === 'edge') {
        this.logger.info('正在调用 Edge-TTS API...')
        await this.callEdgeTTS(testText, config)
      }
      
      this.logger.success(`${providerName} 配置测试成功`)
      this.logger.info('====================================')
      return { success: true }
    } catch (error) {
      const provider = config.provider || 'aliyun'
      const providerName = 
        provider === 'aliyun' ? '阿里云 CosyVoice' : 
        provider === 'tencent' ? '腾讯云 TTS' : 
        'Edge-TTS'
      this.logger.error(`${providerName} 配置测试失败: ${error instanceof Error ? error.message : String(error)}`)
      this.logger.info('====================================')
      return {
        success: false,
        error: error instanceof Error ? error.message : String(error),
      }
    }
  }

  /**
   * 获取阿里云 Token
   * 使用 AccessKey ID 和 AccessKey Secret 获取 Token
   */
  private async getAliyunToken(
    accessKeyId: string,
    accessKeySecret: string,
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      // Token 服务地址 - 使用正确的 OpenAPI 端点
      const host = 'nls-meta.cn-shanghai.aliyuncs.com'
      const path = '/'
      
      // 构建签名所需参数
      const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z')
      const nonce = crypto.randomBytes(16).toString('hex')
      
      const queryParams: Record<string, string> = {
        AccessKeyId: accessKeyId,
        Action: 'CreateToken',
        Format: 'JSON',
        RegionId: 'cn-shanghai',
        SignatureMethod: 'HMAC-SHA1',
        SignatureNonce: nonce,
        SignatureVersion: '1.0',
        Timestamp: timestamp,
        Version: '2019-02-28',
      }
      
      // 按字典序排序参数
      const sortedKeys = Object.keys(queryParams).sort()
      const canonicalizedQueryString = sortedKeys
        .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`)
        .join('&')
      
      // 构建待签名字符串 (按照阿里云 POP API 签名规范)
      const stringToSign = `GET&${encodeURIComponent(path)}&${encodeURIComponent(canonicalizedQueryString)}`
      
      // 计算签名 (注意：accessKeySecret 后面要加 &)
      const signature = crypto
        .createHmac('sha1', `${accessKeySecret}&`)
        .update(stringToSign)
        .digest('base64')
      
      // 构建完整 URL
      const fullUrl = `https://${host}${path}?${canonicalizedQueryString}&Signature=${encodeURIComponent(signature)}`
      
      const chunks: Buffer[] = []
      
      const req = https.get(fullUrl, res => {
        res.on('data', (chunk: Buffer) => {
          chunks.push(chunk)
        })
        
        res.on('end', () => {
          const responseBody = Buffer.concat(chunks).toString()
          
          try {
            const response = JSON.parse(responseBody)
            
            // 检查返回结果
            if (response.Token && response.Token.Id) {
              this.logger.info('Token 获取成功')
              resolve(response.Token.Id)
            } else if (response.Message || response.message) {
              reject(new Error(`获取 Token 失败: ${response.Message || response.message}`))
            } else if (response.Code) {
              reject(new Error(`获取 Token 失败: [${response.Code}] ${response.Message || '未知错误'}`))
            } else {
              reject(new Error(`获取 Token 失败: ${responseBody}`))
            }
          } catch (error) {
            reject(new Error(`解析 Token 响应失败: ${responseBody}`))
          }
        })
      })
      
      req.on('error', error => {
        this.logger.error(`获取 Token 网络请求失败: ${error.message}`)
        reject(new Error(`获取 Token 网络请求失败: ${error.message}`))
      })
      
      req.end()
    })
  }


  /**
   * 生成腾讯云 API 签名
   * 参考文档: https://cloud.tencent.com/document/api/1073/37995
   */
  private generateTencentSignature(
    secretId: string,
    secretKey: string,
    params: any,
    region: string,
  ): Record<string, string> {
    const timestamp = Math.floor(Date.now() / 1000)
    const date = new Date(timestamp * 1000).toISOString().split('T')[0]
    
    // 公共参数
    const service = 'tts'
    const host = 'tts.tencentcloudapi.com'
    const action = 'TextToVoice'
    const version = '2019-08-23'
    const algorithm = 'TC3-HMAC-SHA256'
    const requestPayload = JSON.stringify(params)
    
    // 1. 拼接规范请求串
    const httpRequestMethod = 'POST'
    const canonicalUri = '/'
    const canonicalQueryString = ''
    const canonicalHeaders = `content-type:application/json\nhost:${host}\n`
    const signedHeaders = 'content-type;host'
    const hashedRequestPayload = crypto
      .createHash('sha256')
      .update(requestPayload)
      .digest('hex')
    
    const canonicalRequest = [
      httpRequestMethod,
      canonicalUri,
      canonicalQueryString,
      canonicalHeaders,
      signedHeaders,
      hashedRequestPayload,
    ].join('\n')
    
    // 2. 拼接待签名字符串
    const credentialScope = `${date}/${service}/tc3_request`
    const hashedCanonicalRequest = crypto
      .createHash('sha256')
      .update(canonicalRequest)
      .digest('hex')
    
    const stringToSign = [
      algorithm,
      timestamp,
      credentialScope,
      hashedCanonicalRequest,
    ].join('\n')
    
    // 3. 计算签名
    const secretDate = crypto
      .createHmac('sha256', `TC3${secretKey}`)
      .update(date)
      .digest()
    const secretService = crypto
      .createHmac('sha256', secretDate)
      .update(service)
      .digest()
    const secretSigning = crypto
      .createHmac('sha256', secretService)
      .update('tc3_request')
      .digest()
    const signature = crypto
      .createHmac('sha256', secretSigning)
      .update(stringToSign)
      .digest('hex')
    
    // 4. 拼接 Authorization
    const authorization = `${algorithm} Credential=${secretId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`
    
    return {
      'Content-Type': 'application/json',
      'Host': host,
      'X-TC-Action': action,
      'X-TC-Version': version,
      'X-TC-Timestamp': timestamp.toString(),
      'X-TC-Region': region,
      'Authorization': authorization,
    }
  }

  /**
   * 调用腾讯云 TTS API
   * 参考文档: https://cloud.tencent.com/document/product/1073/37995
   */
  private async callTencentTTS(
    text: string,
    config: AIAssistConfig,
  ): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      const { tencent, voice, broadcast } = config
      const region = tencent.region || 'ap-guangzhou'

      // 获取音色ID
      const voiceType = Number(
        voice.type === 'preset' ? voice.presetVoiceId : voice.cloneVoiceId
      ) || 1001

      // 构建请求参数
      const params = {
        Text: text,
        SessionId: `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        VoiceType: voiceType,
        Volume: broadcast.volume / 100, // ✅ 修正：腾讯云音量范围 0-1（0-100 配置值转换为 0-1）
        Speed: broadcast.speed,
        Codec: 'mp3',
        SampleRate: 16000,
      }

      // 生成签名和请求头
      const headers = this.generateTencentSignature(
        tencent.secretId,
        tencent.secretKey,
        params,
        region,
      )
      
      const requestBody = JSON.stringify(params)
      headers['Content-Length'] = Buffer.byteLength(requestBody).toString()

      const options = {
        hostname: 'tts.tencentcloudapi.com',
        port: 443,
        path: '/',
        method: 'POST',
        headers: headers,
      }

      this.logger.info('===== 腾讯云 TTS 请求 =====')
      this.logger.info('Region:', region)
      this.logger.info('SecretId (前10位):', tencent.secretId?.substring(0, 10) + '...')
      this.logger.info('SecretKey (前10位):', tencent.secretKey?.substring(0, 10) + '...')
      this.logger.info('请求参数:', {
        text: text.substring(0, 50) + (text.length > 50 ? '...' : ''),
        voiceType: voiceType,
        volume: broadcast.volume / 100,
        speed: broadcast.speed,
      })
      this.logger.info('请求头:', {
        'X-TC-Action': headers['X-TC-Action'],
        'X-TC-Version': headers['X-TC-Version'],
        'X-TC-Timestamp': headers['X-TC-Timestamp'],
        'X-TC-Region': headers['X-TC-Region'],
      })
      this.logger.info('=============================')

      const chunks: Buffer[] = []

      const req = https.request(options, res => {
        this.logger.info(`响应状态码: ${res.statusCode}`)
        
        res.on('data', (chunk: Buffer) => {
          chunks.push(chunk)
        })

        res.on('end', () => {
          const responseBody = Buffer.concat(chunks)
          
          try {
            const response = JSON.parse(responseBody.toString())
            
            if (response.Response?.Error) {
              const error = response.Response.Error
              this.logger.error('腾讯云返回错误:', error.Message)
              reject(new Error(`腾讯云 TTS API 错误 [${error.Code}]: ${error.Message}`))
              return
            }
            
            if (response.Response?.Audio) {
              // 腾讯云返回 base64 编码的音频
              const audioBuffer = Buffer.from(response.Response.Audio, 'base64')
              this.logger.success(`成功获取音频数据，大小: ${audioBuffer.length} bytes`)
              resolve(audioBuffer)
            } else {
              this.logger.error('响应中没有音频数据:', response)
              reject(new Error('响应中没有音频数据'))
            }
          } catch (error) {
            this.logger.error('解析响应失败:', error)
            this.logger.error('响应内容:', responseBody.toString().substring(0, 500))
            reject(new Error('解析响应失败'))
          }
        })
      })

      req.on('error', error => {
        this.logger.error('网络请求失败:', error.message)
        reject(new Error(`网络请求失败: ${error.message}`))
      })

      req.write(requestBody)
      req.end()
    })
  }

  /**
   * 调用 Edge-TTS API
   * 仅支持本地服务模式 - 通过 HTTP 调用本地 Edge-TTS 服务
   */
  private async callEdgeTTS(
    text: string,
    config: AIAssistConfig,
  ): Promise<Buffer> {
    const { edge } = config
    
    // 添加日志显示当前配置
    this.logger.info('========== Edge-TTS 调用 ==========')
    this.logger.info('apiUrl:', edge?.apiUrl || 'http://localhost:5000')
    this.logger.info('voice:', edge?.voice)
    this.logger.info('====================================')
    
    // 使用本地服务
    return this.callLocalEdgeTTS(text, config)
  }

  /**
   * 调用本地 Edge-TTS 服务
   * 需要先启动本地 Edge-TTS HTTP 服务
   */
  private async callLocalEdgeTTS(
    text: string,
    config: AIAssistConfig,
  ): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      const { edge, broadcast } = config
      const voice = edge?.voice || 'zh-CN-XiaoxiaoNeural'
      const apiUrl = edge?.apiUrl || 'http://localhost:5000'

      // 计算语速、音量和音调
      const rate = Math.round((broadcast.speed - 1) * 100)
      const rateStr = rate >= 0 ? `+${rate}%` : `${rate}%`
      const volumeStr = `+${Math.round(broadcast.volume)}%`
      const pitchStr = broadcast.pitch >= 0 ? `+${broadcast.pitch}Hz` : `${broadcast.pitch}Hz`

      this.logger.info('===== 本地 Edge-TTS 请求 =====')
      this.logger.info('API 地址:', apiUrl)
      this.logger.info('Voice:', voice)
      this.logger.info('Text:', text.substring(0, 50) + (text.length > 50 ? '...' : ''))
      this.logger.info('=============================')

      const requestBody = JSON.stringify({
        text: text,
        voice: voice,
        rate: rateStr,
        volume: volumeStr,
        pitch: pitchStr,
      })

      const url = new URL('/tts', apiUrl)
      const options = {
        hostname: url.hostname,
        port: url.port || (url.protocol === 'https:' ? 443 : 5000),
        path: url.pathname,
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': Buffer.byteLength(requestBody),
        },
      }

      const chunks: Buffer[] = []
      
      // 根据协议选择 http 或 https
      const requestModule = url.protocol === 'https:' ? https : http

      const req = requestModule.request(options, res => {
        this.logger.info(`响应状态码: ${res.statusCode}`)

        res.on('data', (chunk: Buffer) => {
          chunks.push(chunk)
        })

        res.on('end', () => {
          if (res.statusCode === 200) {
            const audioData = Buffer.concat(chunks)
            this.logger.success(`成功获取音频数据，大小: ${audioData.length} bytes`)
            resolve(audioData)
          } else {
            const errorText = Buffer.concat(chunks).toString()
            this.logger.error('本地服务返回错误:', errorText)
            reject(new Error(`本地 Edge-TTS 服务错误 (${res.statusCode}): ${errorText}`))
          }
        })
      })

      req.on('error', error => {
        this.logger.error('本地服务连接失败:', error.message)
        reject(new Error(`无法连接到本地 Edge-TTS 服务 (${apiUrl}): ${error.message}。请确保服务已启动。`))
      })

      req.write(requestBody)
      req.end()
    })
  }

  /**
   * 直连微软的 Edge-TTS 服务（WebSocket）
   * 可能会遇到 403 错误
   */
  private async callDirectEdgeTTS(
    text: string,
    config: AIAssistConfig,
  ): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      const { edge, broadcast } = config
      const voice = edge?.voice || 'zh-CN-XiaoxiaoNeural'

      // 生成请求 ID
      const requestId = crypto.randomUUID().replace(/-/g, '')
      const timestamp = new Date().toISOString()

      // 尝试不同的端点和参数组合
      // 方法1: 使用 TrustedClientToken（注意大小写）
      const wsUrl = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4&ConnectionId=${requestId}`

      this.logger.info('===== Edge-TTS 请求 =====')
      this.logger.info('Voice:', voice)
      this.logger.info('Text:', text.substring(0, 50) + (text.length > 50 ? '...' : ''))
      this.logger.info('Speed:', broadcast.speed)
      this.logger.info('Volume:', broadcast.volume)
      this.logger.info('Pitch:', broadcast.pitch)
      this.logger.info('RequestId:', requestId)
      this.logger.info('尝试连接:', wsUrl)
      this.logger.info('=============================')

      const ws = new WebSocket(wsUrl, {
        headers: {
          'Accept-Encoding': 'gzip, deflate, br',
          'Accept-Language': 'zh-CN,zh;q=0.9',
          'Cache-Control': 'no-cache',
          'Origin': 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',
          'Pragma': 'no-cache',
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',
        },
      })

      const audioChunks: Buffer[] = []
      let hasReceivedAudio = false

      ws.on('open', () => {
        this.logger.info('WebSocket 连接已建立')

        // 发送配置消息
        const configMessage = `Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":"false","wordBoundaryEnabled":"true"},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}\r\n`
        ws.send(configMessage)

        // 计算语速、音量和音调的调整值
        // Edge-TTS 语速：默认 +0%，范围大约 -50% 到 +100%
        const rate = Math.round((broadcast.speed - 1) * 100) // 0.5-2.0 转为 -50% 到 +100%
        const rateStr = rate >= 0 ? `+${rate}%` : `${rate}%`
        
        // 音量：0-100 转为 0-100
        const volumeStr = `+${Math.round(broadcast.volume)}%`
        
        // 音调：-500 到 +500 转为 Hz
        const pitchStr = broadcast.pitch >= 0 ? `+${broadcast.pitch}Hz` : `${broadcast.pitch}Hz`

        // 发送 SSML 消息
        const ssml = `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="zh-CN"><voice name="${voice}"><prosody pitch="${pitchStr}" rate="${rateStr}" volume="${volumeStr}">${text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}</prosody></voice></speak>`

        const ssmlMessage = `X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nPath:ssml\r\n\r\n${ssml}\r\n`
        ws.send(ssmlMessage)
        
        this.logger.info('已发送 SSML 消息')
      })

      ws.on('message', (data: Buffer) => {
        const message = data.toString('binary', 0, 100)
        
        // 检查是否是音频数据
        if (message.includes('Path:audio')) {
          // 找到音频数据的起始位置
          const separator = Buffer.from('Path:audio\r\n')
          const index = data.indexOf(separator)
          
          if (index !== -1) {
            // 跳过头部，提取音频数据
            const headerEnd = Buffer.from('\r\n\r\n')
            const headerEndIndex = data.indexOf(headerEnd, index)
            if (headerEndIndex !== -1) {
              const audioData = data.subarray(headerEndIndex + 4)
              audioChunks.push(audioData)
              hasReceivedAudio = true
            }
          }
        } else if (message.includes('Path:turn.end')) {
          // 音频传输完成
          this.logger.info('音频传输完成')
          ws.close()
        }
      })

      ws.on('close', () => {
        this.logger.info('WebSocket 连接已关闭')
        
        if (hasReceivedAudio && audioChunks.length > 0) {
          const audioBuffer = Buffer.concat(audioChunks)
          this.logger.success(`成功获取音频数据，大小: ${audioBuffer.length} bytes`)
          resolve(audioBuffer)
        } else {
          reject(new Error('未收到音频数据'))
        }
      })

      ws.on('error', (error) => {
        this.logger.error('WebSocket 错误:', error.message)
        this.logger.error('错误详情:', error)
        reject(new Error(`WebSocket 连接失败: ${error.message}。Edge-TTS 服务可能暂时不可用，请稍后重试或使用阿里云/腾讯云服务。`))
      })

      // 设置超时（30秒）
      setTimeout(() => {
        if (ws.readyState === WebSocket.OPEN) {
          ws.close()
          reject(new Error('请求超时'))
        }
      }, 30000)
    })
  }

  /**
   * 调用阿里云 CosyVoice TTS API
   * 使用 Token 认证方式
   */
  private async callAliyunTTS(
    text: string,
    config: AIAssistConfig,
  ): Promise<Buffer> {
    // 先获取 Token
    const token = await this.getAliyunToken(
      config.aliyun.accessKeyId,
      config.aliyun.accessKeySecret,
    )

    return new Promise((resolve, reject) => {
      const { aliyun, voice, broadcast } = config

      // 构建请求参数
      const voiceId =
        voice.type === 'preset' ? voice.presetVoiceId : voice.cloneVoiceId

      // 阿里云 TTS 参数范围验证和转换
      // speech_rate: -500 到 500 (相对值)
      // pitch_rate: -500 到 500
      // volume: 0 到 100
      const speechRate = Math.round((broadcast.speed - 1) * 500) // 将 0.5-2.0 转换为 -250到500
      const pitchRate = Math.round(broadcast.pitch) // 确保是整数
      const volume = Math.round(broadcast.volume) // 确保是整数
      
      // ✅ 修复: 阿里云 TTS API 完整参数（包含语速、音调、音量）
      const params: any = {
        appkey: aliyun.appKey,
        token: token,
        text,
        voice: voiceId || 'zhixiaobai',
        format: 'mp3',
        speech_rate: speechRate,  // 语速
        pitch_rate: pitchRate,    // 音调
        volume: volume,           // 音量
      }

      // 使用默认 endpoint 或用户自定义的
      const endpoint =
        aliyun.endpoint ||
        'https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts'

      const url = new URL(endpoint)
      const requestBody = JSON.stringify(params)

      const options = {
        hostname: url.hostname,
        port: url.port || 443,
        path: url.pathname,
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': Buffer.byteLength(requestBody),
        },
      }

      const chunks: Buffer[] = []

      const req = https.request(options, res => {
        res.on('data', (chunk: Buffer) => {
          chunks.push(chunk)
        })

        res.on('end', () => {
          const contentType = res.headers['content-type']
          const audioData = Buffer.concat(chunks)
          
          if (res.statusCode === 200 && contentType?.includes('audio')) {
            resolve(audioData)
          } else {
            try {
              const errorData: AliyunTTSResponse = JSON.parse(
                audioData.toString(),
              )
              reject(
                new Error(
                  `TTS API 错误: ${errorData.message || res.statusCode}`,
                ),
              )
            } catch {
              const errorText = audioData.toString()
              reject(new Error(`TTS API 错误: HTTP ${res.statusCode} - ${errorText}`))
            }
          }
        })
      })

      req.on('error', error => {
        reject(new Error(`网络请求失败: ${error.message}`))
      })

      req.write(requestBody)
      req.end()
    })
  }

  /**
   * 播放音频数据 - 在应用内播放
   */
  private async playAudio(audioData: Buffer): Promise<void> {
    // 检查音频数据是否有效
    if (!audioData || audioData.length === 0) {
      throw new Error('音频数据为空')
    }
    
    // 将音频数据转换为 base64，发送到渲染进程播放
    const base64Audio = audioData.toString('base64')
    
    // 通过 BrowserWindow 向渲染进程发送音频数据
    const { BrowserWindow, ipcMain } = await import('electron')
    const mainWindow = BrowserWindow.getAllWindows()[0]
    
    if (!mainWindow) {
      throw new Error('找不到主窗口')
    }
    
    // 发送音频数据到渲染进程
    mainWindow.webContents.send('tts:play-audio', base64Audio)
    
    // 等待渲染进程播放完成
    return new Promise((resolve, reject) => {
      
      // 监听播放完成事件
      const onPlayComplete = () => {
        cleanup()
        resolve()
      }
      
      // 监听播放错误事件
      const onPlayError = (_event: any, error: string) => {
        this.logger.error(`音频播放失败: ${error}`)
        cleanup()
        reject(new Error(error))
      }
      
      // 清理监听器
      const cleanup = () => {
        ipcMain.removeListener('tts:play-complete', onPlayComplete)
        ipcMain.removeListener('tts:play-error', onPlayError)
      }
      
      // 注册监听器
      ipcMain.once('tts:play-complete', onPlayComplete)
      ipcMain.once('tts:play-error', onPlayError)
      
      // 设置超时（60秒，适应更长的话术）
      setTimeout(() => {
        this.logger.warn('播放超时，自动结束')
        cleanup()
        resolve()
      }, 60000)
    })
  }

  /**
   * 停止当前播放
   */
  stop(): void {
    if (this.currentAudio) {
      this.currentAudio.stop()
      this.currentAudio = null
    }
  }

  /**
   * 清理资源
   */
  dispose(): void {
    this.stop()
    if (this.audioContext) {
      this.audioContext.close()
      this.audioContext = null
    }
  }
}

// 单例
let ttsServiceInstance: TTSService | null = null

export function getTTSService(): TTSService {
  if (!ttsServiceInstance) {
    ttsServiceInstance = new TTSService()
  }
  return ttsServiceInstance
}

